/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-13
 * V4.0
 */
package com.jphenix.webserver.instancea;

import com.jphenix.servlet.common.SessionListener;
import com.jphenix.share.tools.Base64;
import com.jphenix.standard.docs.ClassInfo;

import javax.servlet.ServletContext;
import javax.servlet.http.*;
import java.io.*;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

/**
 * 会话处理类
 * 
 * 2019-09-17 整理了代码格式
 * 
 * @author 刘虻
 * 2006-9-15上午12:10:31
 */
@SuppressWarnings("deprecation")
@ClassInfo({"2019-09-17 10:39","会话处理类"})
public class Session extends Hashtable<String,Object> implements HttpSession {
	
	/**
	 * 版本标识
	 */
	private static final long serialVersionUID = 5005301247469044903L;

	private long    createTime;       //建立时间
	private long    lastAccessTime;   //最后访问时间
	private String  id;               //会话主键
	private int     inactiveInterval; //间隔时间
	private boolean expired;          //募集
	
	private transient ServletContext        servletContext; //Servlet上下文
	private transient HttpSessionContext    sessionContext; //会话上下文
	private transient List<SessionListener> listeners;      //会话监听器序列


	/**
	 * 构造函数
	 * 2008-7-8下午06:21:49
	 */
	public Session(String id,ServletContext servletContext,HttpSessionContext sessionContext) {
		this(id, 0, servletContext, sessionContext);
	}

	/**
	 * 构造函数
	 * 2008-7-8下午06:21:53
	 */
	public Session(
			String id
			, int inactiveInterval
			, ServletContext servletContext
			, HttpSessionContext sessionContext) {
		createTime = System.currentTimeMillis();
		this.id = id;
		this.inactiveInterval = inactiveInterval;
		this.servletContext = servletContext;
		this.sessionContext = sessionContext;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:22:22
	 */
	@Override
    public long getCreationTime() {
		return createTime;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:22:27
	 */
	@Override
    public String getId() {
		return id;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:22:31
	 */
	@Override
    public long getLastAccessedTime() {
		return lastAccessTime;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:22:34
	 */
	@Override
    public void setMaxInactiveInterval(int interval) {
		inactiveInterval = interval;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:22:37
	 */
	@Override
    public int getMaxInactiveInterval() {
		return inactiveInterval;
	}

	/**
	 * @deprecated
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:22:48
	 */
	@Override
    public HttpSessionContext getSessionContext() {
		return sessionContext;
	}


	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:22:58
	 */
	@Override
    public ServletContext getServletContext() {
		return servletContext;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:23:05
	 */
	@Override
    public Object getAttribute(String name) throws IllegalStateException {
		if (expired) {
			throw new IllegalStateException();
		}
		return get(name);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:23:33
	 */
	@Override
    public Object getValue(String name) throws IllegalStateException {
		return getAttribute(name);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:23:44
	 */
	@Override
    public Enumeration<String> getAttributeNames() throws IllegalStateException {
		if (expired) {
			throw new IllegalStateException();
		}
		return keys();
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:23:59
	 */
	@Override
    public java.lang.String[] getValueNames() throws IllegalStateException {
		Enumeration<String> e = getAttributeNames();
		Vector<String> names = new Vector<String>();
		while (e.hasMoreElements()) {
			names.addElement(e.nextElement());
		}
		String[] result = new String[names.size()];
		names.copyInto(result);
		return result;
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:24:18
	 */
	@Override
    public void setAttribute(String name, Object value) throws IllegalStateException {
		if (expired) {
			throw new IllegalStateException();
		}
		//获取旧的属性值
		Object oldValue = value!=null?put(name,value):remove(name);
		if (oldValue != null) {
			if (oldValue instanceof HttpSessionBindingListener) {
				((HttpSessionBindingListener) oldValue).valueUnbound(new HttpSessionBindingEvent(this, name));
			}else if (oldValue instanceof HttpSessionAttributeListener) {
				((HttpSessionAttributeListener) oldValue).attributeReplaced(new HttpSessionBindingEvent(this,name,value));
			}
		}
		if (value instanceof HttpSessionBindingListener) {
			((HttpSessionBindingListener) value).valueBound(new HttpSessionBindingEvent(this, name));
		}else if (value instanceof HttpSessionAttributeListener) {
			((HttpSessionAttributeListener) value).attributeAdded(new HttpSessionBindingEvent(this, name));
		}
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:26:42
	 */
	@Override
    public void putValue(String name, Object value) throws IllegalStateException {
		setAttribute(name, value);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:26:47
	 */
	@Override
    public void removeAttribute(java.lang.String name) throws IllegalStateException {
		if (expired) {
            throw new IllegalStateException();
        }
		Object value = remove(name);
		if (value != null) {
            if (value instanceof HttpSessionBindingListener) {
                ((HttpSessionBindingListener) value).valueUnbound(new HttpSessionBindingEvent(this, name));
            } else if (value instanceof HttpSessionAttributeListener) {
                ((HttpSessionAttributeListener) value).attributeRemoved(new HttpSessionBindingEvent(this, name));
            }
        }
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2008-7-8下午06:26:52
	 */
	@Override
    public void removeValue(java.lang.String name) throws IllegalStateException {
		removeAttribute(name);
	}

	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2006-9-15上午09:07:01
	 */
	@Override
    public synchronized void invalidate() throws IllegalStateException {
		if (expired) {
			throw new IllegalStateException();
		}
		notifyListeners();
		Enumeration<String> enumeration = getAttributeNames();
		while (enumeration.hasMoreElements()) {
			removeAttribute(enumeration.nextElement());
		}
		setExpired(true);
	}
	
	/**
	 * 覆盖方法
	 * @author 刘虻
	 * 2006-9-15上午09:07:51
	 */
	@Override
    public boolean isNew() throws IllegalStateException {
		if (expired) {
			throw new IllegalStateException();
		}
		return lastAccessTime == 0;
	}

	/**
	 * 设置监听器
	 * @author 刘虻
	 * 2006-9-15上午09:08:11
	 * @param list
	 */
	public synchronized void setListeners(List<SessionListener> list) {
		if (listeners == null) {
			listeners = list;
			if (listeners != null) {
				HttpSessionEvent event = new HttpSessionEvent(this);
				for (int i = 0; i < listeners.size(); i++) {
                    try {
                        ((HttpSessionListener) listeners.get(0)).sessionCreated(event);
                    } catch (ClassCastException cce) {
                        cce.printStackTrace();
                    } catch (NullPointerException npe) {
                        npe.printStackTrace();
                    }
                }
			}
		}
	}

	/**
	 * 设置Servlet上下文
	 * @author 刘虻
	 * 2008-7-8下午06:28:07
	 * @param sc Servlet上下文
	 */
	public synchronized void setServletContext(ServletContext sc) {
		servletContext = sc;
	}

	/**
	 * 处理监听器
	 * @author 刘虻
	 * 2008-7-8下午06:28:41
	 */
	protected void notifyListeners() {
		if (listeners != null) {
			HttpSessionEvent event = new HttpSessionEvent(this);
			for (int i = 0; i < listeners.size(); i++) {
                try {
                    ((HttpSessionListener) listeners.get(i)).sessionDestroyed(event);
                } catch (ClassCastException cce) {
                } catch (NullPointerException npe) {
                }
            }
		}
	}

	/**
	 * 母鸡
	 * @author 刘虻
	 * 2008-7-8下午06:29:19
	 * @param expired 母鸡
	 */
	protected void setExpired(boolean expired) {
		this.expired = expired;
	}

	/**
	 * 母鸡
	 * @author 刘虻
	 * 2008-7-8下午06:29:31
	 * @return 母鸡
	 */
	protected boolean isValid() {
		return !expired;
	}

	/**
	 * 母鸡
	 * @author 刘虻
	 * 2008-7-8下午06:29:40
	 * @return 母鸡
	 */
	protected boolean checkExpired() {
		return inactiveInterval > 0 && (inactiveInterval * 1000 < System.currentTimeMillis() - lastAccessTime);
	}

	/**
	 * 母鸡
	 * @author 刘虻
	 * 2008-7-8下午06:29:44
	 */
	protected void userTouch() {
		if (isValid()) {
            lastAccessTime = System.currentTimeMillis();
        } else {
            throw new IllegalStateException();
        }
	}

	/**
	 * 母鸡
	 * @author 刘虻
	 * 2008-7-8下午06:29:56
	 * @param w 母鸡
	 * @throws IOException 执行发生异常
	 */
	protected void save(Writer w) throws IOException {
		if (expired) {
            return;
        }
		w.write(id);
		w.write(':');
		w.write(Integer.toString(inactiveInterval));
		w.write(':');
		w.write(servletContext == null || servletContext.getServletContextName() == null ? "" : servletContext.getServletContextName());
		w.write(':');
		w.write(Long.toString(lastAccessTime));
		w.write("\r\n");
		Enumeration<String> e = getAttributeNames();
		ByteArrayOutputStream os = new ByteArrayOutputStream(1024 * 16);
		while (e.hasMoreElements()) {
			String aname = e.nextElement();
			Object so = get(aname);
			if (so instanceof Serializable) {
				os.reset();
				ObjectOutputStream oos = new ObjectOutputStream(os);
				try {
					oos.writeObject(so);
					w.write(aname);
					w.write(":");
					w.write(Base64.base64Encode(os.toByteArray()));
					w.write("\r\n");
				} catch (IOException ioe) {
					ioe.printStackTrace();
					System.out.println("Problem storing session value " + aname);
				}
			}
		}
		w.write("$$\r\n");
	}

	/**
	 * 母鸡
	 * @author 刘虻
	 * 2008-7-8下午06:30:16
	 * @param r 母鸡
	 * @param inactiveInterval 母鸡
	 * @param servletContext 母鸡
	 * @param sessionContext 母鸡
	 * @return 母鸡
	 * @throws IOException 执行发生异常
	 */
	protected static Session restore(BufferedReader r, int inactiveInterval, ServletContext servletContext,
			HttpSessionContext sessionContext) throws IOException {
		String s = r.readLine();
		if (s == null) { // eos
			return null;
		}
		int cp = s.indexOf(':');
		if (cp < 0) {
			throw new IOException("Invalid format for a session header, no session id: " + s);
		}
		String id = s.substring(0, cp);
		int cp2 = s.indexOf(':', cp + 1);
		if (cp2 < 0) {
			throw new IOException("Invalid format for a session header, no latency: " + s);
		}
		try {
			inactiveInterval = Integer.parseInt(s.substring(cp + 1, cp2));
		} catch (NumberFormatException nfe) {
			servletContext.log("Session latency invalid:" + s.substring(cp + 1, cp2) + " " + nfe);
		}
		cp = s.indexOf(':', cp2 + 1);
		if (cp < 0) {
			throw new IOException("Invalid format for a session header, context name: " + s);
		}
		String contextName = s.substring(cp2 + 1, cp);
		// consider servletContext.getContext("/"+contextName)
		Session result = new Session(id, inactiveInterval, contextName.length() == 0 ? servletContext
				: null, sessionContext);
		try {
			result.lastAccessTime = Long.parseLong(s.substring(cp + 1));
		} catch (NumberFormatException nfe) {
			servletContext.log("Last access time invalid:" + s.substring(cp + 1) + " " + nfe);
		}
		do {
			s = r.readLine();
			if (s == null) {
				throw new IOException("Unexpected end of stream.");
			}
			if ("$$".equals(s)) {
				return result;
			}
			cp = s.indexOf(':');
			if (cp < 0) {
				throw new IOException("Invalid format for a session entry: " + s);
			}
			String aname = s.substring(0, cp);
			// if (lazyRestore)
			// result.put(aname, s.substring(cp+1));
			ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(Base64.decode64(s
					.substring(cp + 1))));
			try {
				result.put(aname, ois.readObject());
			} catch (ClassNotFoundException cnfe) {
				servletContext.log("Can't restore :" + aname + ", " + cnfe);
			} catch (IOException ioe) {
				servletContext.log("Can't restore :" + aname + ", " + ioe);
			}
		} while (true);
	}
}